#include "util.h"
#include <iostream>
#include <fstream>
#include <cassert>

using namespace std;

/// input text file parser
TestMap::TestMap(const char *filename) : map_size_(0)
{
  assert(filename);
  ifstream ifstrm(filename);
  if (!ifstrm.good())
    {
      cerr << "Error: cannot open file: " << filename << endl;
    }
  else
    {
      size_t lineNo = 0; // record the map line number starting from 0
      while (ifstrm.good())
	{
	  string line;
	  getline(ifstrm, line);
	  lineNo = parseLine(line, lineNo);
	}
    }
}

size_t TestMap::parseLine(const string &str, const size_t currentLineNo)
{
  // skip an empty line or a comment line starting with a "#"
  if (*str.begin() == '#' || str.empty())
    return currentLineNo;
  MapLineType mline; // create a buffer for a new map line
  const size_t row = currentLineNo; // map row cursor
  size_t col = 0; // map column cursor
  for (string::const_iterator it = str.begin(); it != str.end(); ++it, ++col)
    {
      MapElementType type(UNKNOWN_MAP_ELEMENT_TYPE);
      switch (*it)
	{
	case '.': type = ROAD;  break;
	case 'X':
	case 'x': type = BLOCK;
	break;
	case 'S':
	case 's': type = START;
		  start_ = Position(row, col); // record START
	break;
	case 'G':
	case 'g': type = GOAL;
		  goal_  = Position(row, col); // record GOAL
	break;
	default:
		  assert(!"Unknown map element!");
	}
      mline.push_back(type);
    }
  map_.push_back(mline); // load buffer
  map_size_ += mline.size(); // update map size
  // only square maps are supported
  assert("Map is not square!" && ((map_.size() <= 1)
	   || (map_.at(currentLineNo - 1).size()
	       == map_.at(currentLineNo).size())));
  return currentLineNo + 1; // next map line number
}

void TestMap::print(const std::string &banner) const
{
  if (!banner.empty())
    cout << banner << endl;
  for (MapType::const_iterator lineIter = map_.begin();
       lineIter != map_.end(); ++lineIter)
    {
      for (MapLineType::const_iterator it = lineIter->begin();
	   it != lineIter->end(); ++it) // print each line
	cout << *it;
      cout << endl;
    }
}
